//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <drvpub.h>
#include <xllp_dmac.h>
#include <xllp_gpio.h>
#include <xllp_clkmgr.h>
#include <bulverde.h>
#include "ccamera.h"

/***********************************************************************
 * Declarations
 ***********************************************************************/

// map of xllp camera image format (xllp_camera.h) ==> capture interface format (xllp_ci.h)
static const XLLP_CI_IMAGE_FORMAT FORMAT_MAPPINGS[] = {
        XLLP_CI_RAW8,                   //RAW                          0
        XLLP_CI_RAW9,                   //                             1
        XLLP_CI_RAW10,                  //                             2

        XLLP_CI_RGB444,                 //RGB                          3
        XLLP_CI_RGB555,                 //                             4
        XLLP_CI_RGB565,                 //                             5
        XLLP_CI_RGB666_PACKED,          //RGB Packed                   6
        XLLP_CI_RGB666,                 //                             7
        XLLP_CI_RGB888_PACKED,          //                             8
        XLLP_CI_RGB888,                 //                             9
        XLLP_CI_RGBT555_0,              //RGB+Transparent bit 0        10
        XLLP_CI_RGBT888_0,              //                             11
        XLLP_CI_RGBT555_1,              //RGB+Transparent bit 1        12
        XLLP_CI_RGBT888_1,              //                             13

        XLLP_CI_INVALID_FORMAT,         //                             14
        XLLP_CI_YCBCR422,               //YCBCR                        15
        XLLP_CI_YCBCR422_PLANAR,        //YCBCR Planaried              16
        XLLP_CI_INVALID_FORMAT,         //                             17
        XLLP_CI_INVALID_FORMAT          //                             18
};


/*************************CI Operation Function*************************/
void CIInit()
{
    // clear all CI registers
    CICR0 = 0x3FF;   // disable all interrupts
    CICR1 = 0;
    CICR2 = 0;
    CICR3 = 0;
    CICR4 = 0;
    CISR = ~0;
    CIFR = 0;
    CITOR = 0;

    // enable CI clock
    CKEN |= XLLP_CLKEN_CAMERA/*(0x1u << 24)*/;
}

void CIDeInit()
{
    // disable CI clock
    CKEN &= ~XLLP_CLKEN_CAMERA;
}

void CIEnable(int dma_en)
{
    unsigned int value;

    // write mask in cicr0
    value = CICR0;
    value |= XLLP_CI_CICR0_ENB;
    value &= ~XLLP_CI_CICR0_DIS;
    if (dma_en) {
        value |= XLLP_CI_CICR0_DMA_EN;
    }
    CICR0 = value;
    DBGOUT(kprintf("CI enable\n"));
}

int CIDisable(int quick)
{
	volatile unsigned int value, mask;
	//int retry;

	// write control bit in cicr0
	value = CICR0;
	if (quick) {
		value &= ~XLLP_CI_CICR0_ENB;
		mask = XLLP_CI_CISR_CQD;
	}
	else {
		value |= XLLP_CI_CICR0_DIS;
		mask = XLLP_CI_CISR_CDD;
	}
	CICR0 = value;
	return -1;
}

void CISetMode(XLLP_CI_MODE mode, XLLP_CI_DATA_WIDTH data_width)
{
    unsigned int value;

    // write mode field in cicr0
    value = CICR0;

    //set 0b000 = Master-parallel mode
    value &= ~(XLLP_CI_CICR0_SIM_SMASK << XLLP_CI_CICR0_SIM_SHIFT/*Sensor interface Mode*/);
    value |= (unsigned int)mode << XLLP_CI_CICR0_SIM_SHIFT;
    CICR0 = value;

    // write data width cicr1
    value = CICR1;
    value &= ~(XLLP_CI_CICR1_DW_SMASK << XLLP_CI_CICR1_DW_SHIFT);
    value |= ((unsigned)data_width) << XLLP_CI_CICR1_DW_SHIFT;
    CICR1 = value;
    return;
}

void CISetClock(int pclk_enable, int mclk_enable, unsigned int mclk_khz)
{
    unsigned int ciclk,  value, div, cccr_l;

    // determine the LCLK frequency programmed into the CCCR.
    cccr_l = CCCR & 0x0000001F;

	if (cccr_l < 8) // L = [2 - 7]
		ciclk = (13 * cccr_l) * 100;
	else if (cccr_l < 17) // L = [8 - 16]
		ciclk = ((13 * cccr_l) * 100) >> 1;
	else // if (cccr_l < 32) // L = [17 - 31]
		ciclk = ((13 * cccr_l) * 100) >> 2;
    DBGOUT(kprintf("ciclk = 0x%x\n", ciclk));

	// want a divisor that gives us a clock rate as close to, but not more than the given mclk.
	div = (ciclk / (2 * mclk_khz))-1;
//    div = 1;
    DBGOUT(kprintf("div = 0x%x\n", div));

    // write cicr4
    value = CICR4;
    value &= ~(XLLP_CI_CICR4_PCLK_EN | XLLP_CI_CICR4_MCLK_EN | XLLP_CI_CICR4_DIV_SMASK<<XLLP_CI_CICR4_DIV_SHIFT);
    value |= (pclk_enable) ? XLLP_CI_CICR4_PCLK_EN : 0;
    value |= (mclk_enable) ? XLLP_CI_CICR4_MCLK_EN : 0;
    value |= div << XLLP_CI_CICR4_DIV_SHIFT;
    CICR4 = value;
    DBGOUT(kprintf("CICR4 = 0x%x\n", CICR4));

    return;
}

void CISetPolarity(int pclk_sample_falling, int hsync_active_low, int vsync_active_low)
{
    unsigned int value;

    // write cicr4
    value = CICR4;
    value &= ~(XLLP_CI_CICR4_PCP | XLLP_CI_CICR4_HSP | XLLP_CI_CICR4_VSP);
    value |= (pclk_sample_falling)? XLLP_CI_CICR4_PCP : 0;
    value |= (hsync_active_low) ? XLLP_CI_CICR4_HSP : 0;
    value |= (vsync_active_low) ? XLLP_CI_CICR4_VSP : 0;
    CICR4 = value;
    return;
}

void CISetFIFO(unsigned int timeout, XLLP_CI_FIFO_THRESHOLD threshold, int fifo1_enable,
                   int fifo2_enable)
{
    unsigned int value;

    // write citor
    CITOR = timeout;

    // write cifr: always enable fifo 0! also reset input fifo
    value = CIFR;
    value &= ~(XLLP_CI_CIFR_FEN0 | XLLP_CI_CIFR_FEN1 | XLLP_CI_CIFR_FEN2 | XLLP_CI_CIFR_RESETF |
                XLLP_CI_CIFR_THL_0_SMASK<<XLLP_CI_CIFR_THL_0_SHIFT);
    value |= (unsigned int)threshold << XLLP_CI_CIFR_THL_0_SHIFT;
    value |= (fifo1_enable) ? XLLP_CI_CIFR_FEN1 : 0;
    value |= (fifo2_enable) ? XLLP_CI_CIFR_FEN2 : 0;
    value |= XLLP_CI_CIFR_RESETF | XLLP_CI_CIFR_FEN0;
    CIFR = value;
    return;
}

void CIResetFIFO()
{
    unsigned int value;
    value = CIFR;
    value |= XLLP_CI_CIFR_RESETF;
    CIFR = value;
}

static void CISetImageFormat(XLLP_CI_IMAGE_FORMAT input_format, XLLP_CI_IMAGE_FORMAT output_format)
{
    unsigned int value, tbit, rgbt_conv, rgb_conv, rgb_f, ycbcr_f, rgb_bpp, raw_bpp, cspace;

    // write cicr1: preserve ppl value and data width value
    value = CICR1;
    value &= ((XLLP_CI_CICR1_PPL_SMASK<<XLLP_CI_CICR1_PPL_SHIFT) |
             ((XLLP_CI_CICR1_DW_SMASK)<<XLLP_CI_CICR1_DW_SHIFT));
    tbit = rgbt_conv = rgb_conv = rgb_f = ycbcr_f = rgb_bpp = raw_bpp = cspace = 0;
    switch(input_format) {
        case XLLP_CI_RAW8:
            cspace = 0;
            raw_bpp = 0;
            break;
        case XLLP_CI_RAW9:
            cspace = 0;
            raw_bpp = 1;
            break;
        case XLLP_CI_RAW10:
            cspace = 0;
            raw_bpp = 2;
            break;
        case XLLP_CI_YCBCR422:
        case XLLP_CI_YCBCR422_PLANAR:
            cspace = 2;
            if (output_format == XLLP_CI_YCBCR422_PLANAR) {
                ycbcr_f = 1;
            }
            break;
        case XLLP_CI_RGB444:
            cspace = 1;
            rgb_bpp = 0;
            break;
        case XLLP_CI_RGB555:
            cspace = 1;
            rgb_bpp = 1;
            if (output_format == XLLP_CI_RGBT555_0) {
                rgbt_conv = 2;
                tbit = 0;
            }
            else if (output_format == XLLP_CI_RGBT555_1) {
                rgbt_conv = 2;
                tbit = 1;
            }
            break;
        case XLLP_CI_RGB565:
            cspace = 1;
            rgb_bpp = 2;
            rgb_f = 1;
            break;
        case XLLP_CI_RGB666:
            cspace = 1;
            rgb_bpp = 3;
            if (output_format == XLLP_CI_RGB666_PACKED) {
                rgb_f = 1;
            }
            break;
        case XLLP_CI_RGB888:
        case XLLP_CI_RGB888_PACKED:
            cspace = 1;
            rgb_bpp = 4;
            switch(output_format) {
            case XLLP_CI_RGB888_PACKED:
                rgb_f = 1;
                break;
            case XLLP_CI_RGBT888_0:
                rgbt_conv = 1;
                tbit = 0;
                break;
            case XLLP_CI_RGBT888_1:
                rgbt_conv = 1;
                tbit = 1;
                break;
            case XLLP_CI_RGB666:
                rgb_conv = 1;
                break;
            case XLLP_CI_RGB565:
                rgb_conv = 2;
                break;
            case XLLP_CI_RGB555:
                rgb_conv = 3;
                break;
            case XLLP_CI_RGB444:
                rgb_conv = 4;
                break;
            default:
                break;
            }
            break;
        default:
            break;
    }
    value |= (tbit==1) ? XLLP_CI_CICR1_TBIT : 0;
    value |= rgbt_conv << XLLP_CI_CICR1_RGBT_CONV_SHIFT;
    value |= rgb_conv << XLLP_CI_CICR1_RGB_CONV_SHIFT;
    value |= (rgb_f==1) ? XLLP_CI_CICR1_RBG_F : 0;
    value |= (ycbcr_f==1) ? XLLP_CI_CICR1_YCBCR_F : 0;
    value |= rgb_bpp << XLLP_CI_CICR1_RGB_BPP_SHIFT;
    value |= raw_bpp << XLLP_CI_CICR1_RAW_BPP_SHIFT;
    value |= cspace << XLLP_CI_CICR1_COLOR_SP_SHIFT;
    CICR1 = value;

    return;
}

static void CIConfigureMP(unsigned int PPL, unsigned int LPF, XLLP_CI_MP_TIMING* timing)
{
    unsigned int value;

    // write ppl(Pixels per Line for the Frame) field in cicr1
    value = CICR1;
    value &= ~(XLLP_CI_CICR1_PPL_SMASK << XLLP_CI_CICR1_PPL_SHIFT);
    value |= (PPL & XLLP_CI_CICR1_PPL_SMASK) << XLLP_CI_CICR1_PPL_SHIFT;
    CICR1 = value;

    // write BLW(Beginning-of-Line Pixel Clock Wait Count), ELW in cicr2
    value = CICR2;
    value &= ~(XLLP_CI_CICR2_BLW_SMASK << XLLP_CI_CICR2_BLW_SHIFT | XLLP_CI_CICR2_ELW_SMASK << XLLP_CI_CICR2_ELW_SHIFT );
    value |= (timing->BLW & XLLP_CI_CICR2_BLW_SMASK) << XLLP_CI_CICR2_BLW_SHIFT;
    CICR2 = value;

    // write BFW(Beginning-of-Frame Line Clock Wait Count), LPF(Lines per Frame) in cicr3
    value = CICR3;
    value &= ~(XLLP_CI_CICR3_BFW_SMASK << XLLP_CI_CICR3_BFW_SHIFT | XLLP_CI_CICR3_LPF_SMASK << XLLP_CI_CICR3_LPF_SHIFT );
    value |= (timing->BFW & XLLP_CI_CICR3_BFW_SMASK) << XLLP_CI_CICR3_BFW_SHIFT;
    value |= (LPF & XLLP_CI_CICR3_LPF_SMASK) << XLLP_CI_CICR3_LPF_SHIFT;
    CICR3 = value;
    return;
}

void CISetInterruptMask(unsigned int mask)
{
    unsigned int value;

    // write mask in cicr0
    value = CICR0;
    value &= ~XLLP_CI_CICR0_INTERRUPT_MASK;
    value |= (mask & XLLP_CI_CICR0_INTERRUPT_MASK);
    CICR0 = value;
    return;
}

EXTERN_C void CISetFrameRate(XLLP_CI_FRAME_CAPTURE_RATE frate)
{
    unsigned int value;

    // write cicr4
    value = CICR4;
    value &= ~(XLLP_CI_CICR4_FR_RATE_SMASK << XLLP_CI_CICR4_FR_RATE_SHIFT);
    value |= (unsigned)frate << XLLP_CI_CICR4_FR_RATE_SHIFT;
    CICR4 = value;
}

static void CIClearInterruptStatus(unsigned int status)
{
    // write 1 to clear
    CISR = status;
}

// Set the image format
ECode CISetCaptureFormat(PCamDrv_Context pCameraCxt)
{
    XLLP_CI_IMAGE_FORMAT ci_input_format, ci_output_format;
    XLLP_CI_MP_TIMING timing;

    //set capture interface
    if (pCameraCxt->InputFormat >  XLLP_CAMERA_IMAGE_FORMAT_MAX/*18*/ ||
        pCameraCxt->OutputFormat > XLLP_CAMERA_IMAGE_FORMAT_MAX )
        return E_ACCESS_DENIED;
    ci_input_format = FORMAT_MAPPINGS[pCameraCxt->InputFormat];
    ci_output_format = FORMAT_MAPPINGS[pCameraCxt->OutputFormat];
    if (ci_input_format == XLLP_CI_INVALID_FORMAT || ci_output_format == XLLP_CI_INVALID_FORMAT)
        return E_ACCESS_DENIED;
    CISetImageFormat(ci_input_format, ci_output_format);

#ifdef PARAM_TEST
    timing.BFW = pCameraCxt->BFW;
    timing.BLW = pCameraCxt->BLW;
#else
    // TEST CODE
    timing.BFW = 1;
    timing.BLW = 1;
#endif

    if (pCameraCxt->CaptureStatus == CamDrv_Status_Still) {
        CIConfigureMP(pCameraCxt->Resolution.StillWidth - 1,
                pCameraCxt->Resolution.StillHeight - 1, &timing);
        CISetFrameRate((XLLP_CI_FRAME_CAPTURE_RATE)0x0);
    }
    else {
        CIConfigureMP(pCameraCxt->Resolution.VideoWidth - 1,
                pCameraCxt->Resolution.VideoHeight - 1, &timing);
        CISetFrameRate((XLLP_CI_FRAME_CAPTURE_RATE)0x0); // Capture all incoming frames
    }
    return NOERROR;
}

void CIStartCapture(PCamDrv_Context pCameraCxt)
{
    CIResetFIFO();
    CIClearInterruptStatus(0xFFFFFFFF);
}

void CIStopCapture()
{
    // Close Quick Capture Interface
    CIDisable(0);
    return;
}

ECode CIPrepare(PCamDrv_Context pCameraCxt)
{
    CISetCaptureFormat(pCameraCxt);
    CISetInterruptMask(0x3f9 | 0x0400); // 0x3f9 | 0x0400 ?
    return NOERROR;
}

void CIDumpRegisters()
{
    kprintf("Dump registers of Quick Capture Interface:\n");
    kprintf("CICR0 = 0x%08x\n", CICR0);
    kprintf("CICR1 = 0x%08x\n", CICR1);
    kprintf("CICR2 = 0x%08x\n", CICR2);
    kprintf("CICR3 = 0x%08x\n", CICR3);
    kprintf("CICR4 = 0x%08x\n", CICR4);
    kprintf("CISR  = 0x%08x\n", CISR);
    kprintf("CIFR  = 0x%08x\n", CIFR);
    kprintf("CITOR = 0x%08x\n", CITOR);
    kprintf("CIBR0 = 0x%08x\n", CIBR0);
    kprintf("CIBR1 = 0x%08x\n", CIBR1);
    kprintf("CIBR2 = 0x%08x\n", CIBR2);
    kprintf("\n");
}

#if 0
static void dump(byte* data, int size)
{
    int i,j,k;
    char visdata[17];
    visdata[16] = '\0';

    for (i=0;i<size;) {
        // print line head
        DBGOUT(kprintf("%08x: ",data + i));
        // print data
        for(k = 0; k < 16; i++, k++) {
            j = i % 16;
            if(i < size) {
                DBGOUT(kprintf("%02x ", data[i]));
                visdata[j] = (data[i] >= (char)0x20 && data[i] <= (char)0x80) ?
                			data[i] : '.';
            }
            else {
                visdata[j] = '\0';
               break;
            }
        }
        // print visible character
        DBGOUT(kprintf("  %s\n",visdata));
    }
}
#endif

